
#ifndef __LIBERTY_LIBRARY_LAZYMUTEXPROVIDER_H__
#define __LIBERTY_LIBRARY_LAZYMUTEXPROVIDER_H__

#include <boost/smart_ptr/detail/spinlock.hpp>

namespace Liberty { namespace Library { namespace Mutex {
    namespace Impl { namespace Lazy {
        template <typename MutexProvider> class Mutex;
        template <typename MutexProvider> class MutexProvider;
        template <typename MutexProvider> class SharedLock;
        template <typename MutexProvider> class UniqueLock;
        template <typename MutexProvider> class UpgradableLock;
        template <typename MutexProvider> class UpgradedLock;

        template <typename MutexProvider>
        class SharedLock {
        protected:
            typedef typename MutexProvider::Mutex                  Mutex;
            typedef std::shared_ptr<typename MutexProvider::Mutex> SharedMutex;
            typedef typename MutexProvider::SharedLock             Base;

        protected:
            SharedMutex _mutex;
            Base*       _lock;

        public:
            SharedLock(Mutex<MutexProvider>& mutex) : _mutex(mutex.mutex()) {
                _lock = new Base(*_mutex);
            }

            ~SharedLock() {
                delete _lock;
            }
        };

        template <typename MutexProvider>
        class UniqueLock {
        protected:
            typedef typename MutexProvider::Mutex                  Mutex;
            typedef std::shared_ptr<typename MutexProvider::Mutex> SharedMutex;
            typedef typename MutexProvider::UniqueLock             Base;

        protected:
            SharedMutex _mutex;
            Base*       _lock;

        public:
            UniqueLock(Mutex<MutexProvider>& mutex) : _mutex(mutex.mutex()) {
                _lock = new Base(*_mutex);
            }

            ~UniqueLock() {
                delete _lock;
            }
        };

        template <typename MutexProvider>
        class UpgradableLock {
        protected:
            typedef typename MutexProvider::Mutex                  Mutex;
            typedef std::shared_ptr<typename MutexProvider::Mutex> SharedMutex;
            typedef typename MutexProvider::UpgradableLock         Base;

        protected:
            SharedMutex _mutex;
            Base*       _lock;

        public:
            UpgradableLock(Mutex<MutexProvider>& mutex) : _mutex(mutex.mutex()) {
                _lock = new Base(*_mutex);
            }

            ~UpgradableLock() {
                delete _lock;
            }

            inline const SharedMutex& mutex() const { return _mutex; }
            inline Base&              lock()  const { return *_lock; }
        };

        template <typename MutexProvider>
        class UpgradedLock {
        protected:
            typedef typename MutexProvider::Mutex                  Mutex;
            typedef std::shared_ptr<typename MutexProvider::Mutex> SharedMutex;
            typedef typename MutexProvider::UpgradedLock           Base;

        protected:
            SharedMutex _mutex;
            Base*       _lock;

        public:
            UpgradedLock(UpgradableLock<MutexProvider>& lock) : _mutex(lock.mutex()) {
                _lock = new Base(lock.lock());
            }

            ~UpgradedLock() {
                delete _lock;
            }
        };

        template <typename MutexProvider>
        class Mutex {
        protected:
            typedef typename MutexProvider::Mutex                  Mutex;
            typedef std::shared_ptr<typename MutexProvider::Mutex> SharedMutex;
            typedef std::weak_ptr<typename MutexProvider::Mutex>   WeakMutex;

            WeakMutex _mutex;

        public:
            SharedMutex mutex() {
                SharedMutex m(_mutex.lock());

                if (!m) {
                    boost::detail::spinlock_pool<888>::scoped_lock lock(this);

                    m = _mutex.lock();

                    if (!m) {
                        _mutex = m = SharedMutex(new Mutex());
                    }
                }
            }
        };
    }}

    template <typename MutexProvider>
    class Lazy {
    public:
        typedef Liberty::Library::Mutex::Impl::Lazy::Mutex<MutexProvider>          Mutex;
        typedef Liberty::Library::Mutex::Impl::Lazy::SharedLock<MutexProvider>     SharedLock;
        typedef Liberty::Library::Mutex::Impl::Lazy::UniqueLock<MutexProvider>     UniqueLock;
        typedef Liberty::Library::Mutex::Impl::Lazy::UpgradableLock<MutexProvider> UpgradableLock;
        typedef Liberty::Library::Mutex::Impl::Lazy::UpgradedLock<MutexProvider>   UpgradedLock;
    };
}}}

#endif
